/*
 * Copyright 2011 Dim
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package org.ratingviewer.data.source;

import java.io.*;
import java.text.MessageFormat;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.io.*;
import org.apache.http.*;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.htmlparser.lexer.Page;
import org.ratingviewer.data.PageGetException;

public class BeregDataSource implements DataSource {

	private HttpClient httpClient;

	public BeregDataSource() {
		super();
	}

	private synchronized HttpClient getHttpClient() {
		if (httpClient == null)
			httpClient = new DefaultHttpClient();
		return httpClient;
	}

	@Override
	public Page findPlayer(String caseSensitiveName) throws PageGetException {
		HttpPost method = null;
		try {
			method = createFindMethodFor(caseSensitiveName);
		} catch (UnsupportedEncodingException ex) {
			throw new PageGetException(MessageFormat.format("Can''t create HTTP method: {0}.",
					ex.getLocalizedMessage()), ex);
		}

		return getPage(method);
	}

	private Page getPage(HttpPost request) throws PageGetException {
		try {
			HttpClient client = getHttpClient();
			HttpResponse response = client.execute(request);
			HttpEntity entity = response.getEntity();
			StatusLine statusLine = response.getStatusLine();
			int responseCode = statusLine.getStatusCode();
			if (HttpStatus.SC_OK == responseCode && entity != null) {
				String charSet = EntityUtils.getContentCharSet(entity);
				InputStream responseBody = entity.getContent();

				return createInMemoryPage(responseBody, charSet);
			} else
				throw new PageGetException(MessageFormat.format("Response status code is not OK (200): {0}.",
						responseCode));
		} catch (ClientProtocolException ex) {
			throw new PageGetException(MessageFormat.format("Can''t retrive HTML page: {0}.",
					ex.getLocalizedMessage()), ex);
		} catch (ParseException ex) {
			throw new PageGetException(MessageFormat.format("Unknown HTML page encoding: {0}.",
					ex.getLocalizedMessage()), ex);
		} catch (IllegalStateException ex) {
			throw new PageGetException(MessageFormat.format("Can''t retrive HTML page stream: {0}.",
					ex.getLocalizedMessage()), ex);
		} catch (UnsupportedEncodingException ex) {
			throw new PageGetException(MessageFormat.format("Can''t create Page with given charset: {0}.",
					ex.getLocalizedMessage()), ex);
		} catch (IOException ex) {
			throw new PageGetException(MessageFormat.format("I/O error occurred on HTML page retrive: {0}.",
					ex.getLocalizedMessage()), ex);
		} finally {
			HttpEntity entity = request.getEntity();
			try {
				EntityUtils.consume(entity);
			} catch (IOException e) {
				// ignored
			}
		}
	}

	private Page createInMemoryPage(InputStream responseBody, String charSet) throws IOException {
		if (charSet == null)
			charSet = HTTP.ISO_8859_1;

		StringWriter stringWriter = new StringWriter(1024);
		IOUtils.copy(responseBody, stringWriter, charSet != null ? charSet : HTTP.ISO_8859_1);
		String pageContent = stringWriter.toString();

		return new Page(pageContent, charSet);
	}

	private HttpPost createFindMethodFor(String caseSensitiveName) throws UnsupportedEncodingException {
		HttpPost method = new HttpPost("http://bereg.ru/chess/club/reiting");

		List<NameValuePair> nvPairs = new LinkedList<NameValuePair>();
		nvPairs.add(new BasicNameValuePair("name", caseSensitiveName));
		nvPairs.add(new BasicNameValuePair("players", "all"));

		method.setEntity(new UrlEncodedFormEntity(nvPairs, HTTP.ISO_8859_1));
		return method;
	}

}
